# -*- coding:utf-8 -*-

# @File     :hte_formatter
# @Author   :Yupeng
# @Date     :2018/11/12 18:45
'''
该模块主要作用是将mongodb的数据进行处理，使其适合于导入ES中
'''
import base64
import datetime
import re

from uuid import UUID
from math import isnan, isinf
from enum import Enum

import logging

LOG = logging.getLogger(__name__)

import bson
import bson.json_util

from mongo_connector.compat import PY3
from mongo_connector.doc_managers.formatters import DocumentFormatter

if PY3:
    long = int
    unicode = str

RE_TYPE = type(re.compile(''))
try:
    from bson.regex import Regex

    RE_TYPES = (RE_TYPE, Regex)
except ImportError:
    RE_TYPES = (RE_TYPE,)


class DynamicType(Enum):
    null = 0
    text = 1
    num = 2
    range_interval = 300
    range_error = 301
    pic = 4
    file = 5
    choice = 6
    list = 7
    table = 8
    container = 9
    generator = 10


class DocumentLister(DocumentFormatter):
    '''
    将Mongodb记录的文档列表化,才能适合于导入到es中
    Transfer complicated dict to a list of simple dict
    {"name": "Alice", "age": 1234}
    => {"p": [{"key": "name", "text": "Alice"}, {"key": "age", "num": 1234} ] }

    '''
    @staticmethod
    def _is_preset(key):
        return key[0] == '_' or key == 'id'

    def transform_value(self, value):
        if isinstance(value, dict):
            if ('lb' in value or 'ub' in value) and 'var' not in value and 'err' not in value:
                return value
            elif ('val' in value or 'err' in value) and 'lb' not in value and 'ub' not in value:
                return value
            else:
                raise ValueError('Illegal data type of not known dynamic type.')
        elif isinstance(value, list):
            return {"list": [dict(self.transform_value(item).items(), index=i+1) for i, item in enumerate(value)]}
        elif isinstance(value, RE_TYPES):
            flags = ""
            if value.flags & re.IGNORECASE:
                flags += "i"
            if value.flags & re.LOCALE:
                flags += "l"
            if value.flags & re.MULTILINE:
                flags += "m"
            if value.flags & re.DOTALL:
                flags += "s"
            if value.flags & re.UNICODE:
                flags += "u"
            if value.flags & re.VERBOSE:
                flags += "x"
            pattern = value.pattern
            raise ValueError('Illegal data type of "/%s/%s" as RE_TYPE.' % (pattern, flags))
            # # quasi-JavaScript notation (may include non-standard flags)
            # return 'value_re', '/%s/%s' % (pattern, flags)
        elif (isinstance(value, bson.Binary) or
              (PY3 and isinstance(value, bytes))):
            # Just include body of binary data without subtype
            raise ValueError('Illegal data type of "%s" as bson.Binary.' % base64.b64encode(value).decode())
        elif isinstance(value, bson.objectid.ObjectId):
            return {"id": str(value)}
        elif isinstance(value, UUID):
            raise ValueError('Illegal data type of "%s" as UUID.' % str(value))
            # return DynamicType.container
        elif isinstance(value, (int, long, float)):
            if isnan(value):
                raise ValueError("nan")
            elif isinf(value):
                raise ValueError("inf")
            return {"num": value}
        elif isinstance(value, datetime.datetime):
            raise ValueError('Illegal data type of "%s" as datatime.datatime.' % value.strftime("%Y-%m-%d"))
            # return 'value_time', value
        elif value is None:
            return {"null": value}
        # Default
        else:
            if len(value) == 0:
                return {"text": None}
            else:
                return {"text": value}

    def transform_element(self, key, value):
        try:
            new_value = self.transform_value(value)
            new_value['key'] = key
            return new_value
        except ValueError as e:
            LOG.warning("Invalid value for key: %s as %s"
                     % (key, str(e)))

    def format_document(self, doc):
        def get_dynamic(document):
            for key in document:
                if not self._is_preset(key):
                    value = document[key]
                    yield self.transform_element(key, value)

        def get_preset(document):
            for key in document:
                if self._is_preset(key):
                    value = document[key]
                    yield key, str(value) if isinstance(value, bson.objectid.ObjectId) else value
        return dict(get_preset(doc), p=list(get_dynamic(doc)))



























